package com.example.photo.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.photo.common.ErrorCode;
import com.example.photo.config.RestTemplateConfig;
import com.example.photo.exception.BusinessException;
import com.example.photo.mapper.UserMapper;
import com.example.photo.model.VO.UserVO;
import com.example.photo.model.domain.User;
import com.example.photo.service.UserService;
import com.example.photo.utils.AvatarUtils;
import com.example.photo.utils.JwtTokenUtils;
import com.example.photo.utils.VerCodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author 85792
 * @description 针对表【user(用户)】的数据库操作Service实现
 * @createDate 2023-03-25 19:15:40
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {

    public static final String SALT = "news_photo";

    public static final String LOGIN_URL = "https://api.twt.edu.cn/api/auth/common";

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private RestTemplateConfig restTemplateConfig;

    @Resource
    JavaMailSenderImpl mailSender;

    @Resource
    private UserMapper userMapper;


    @Resource
    private Environment environment;

    /**
     * 普通用户使用账号注册
     * 输入两次密码（确认密码）
     *
     * @param username      账号
     * @param password      密码
     * @param checkPassword 校验密码
     * @return 用户信息
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    @Override
    public UserVO commonUserRegister(String username, String password, String checkPassword,
                                     HttpServletResponse response) {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        response.setStatus(HttpServletResponse.SC_OK);

        if (!password.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
        }
        User user = new User();

        synchronized (username.intern()) {
            // 账号不能重复
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("username", username);
            long count = userMapper.selectCount(queryWrapper);
            if (count > 0) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号重复");
            }

            String encryptPassword = DigestUtils.md5DigestAsHex((SALT + password).getBytes());
            user.setUsername(username);
            user.setPassword(encryptPassword);
            //生成随机头像
            user.setAvatar(AvatarUtils.getAvatarUrl());
            boolean result = this.save(user);
            if (!result) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "插入用户失败");
            }
        }

        UserVO userVO = new UserVO(user);
        response.setHeader(JwtTokenUtils.TOKEN_HEADER, JwtTokenUtils.generateToken(userVO));
        return userVO;
    }

    /**
     * 账号密码登录，查本地数据库
     * 普通用户和管理员都可走这个接口
     * 天外天账户登录过一次后走这个接口也会成功
     *
     * @param username 账号
     * @param password 密码
     * @return 用户信息
     */
    @Override
    public UserVO commonUserLogin(String username, String password, HttpServletResponse response) {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        response.setStatus(HttpServletResponse.SC_OK);

        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + password).getBytes());

        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        queryWrapper.eq("password", encryptPassword);
        User user = userMapper.selectOne(queryWrapper);

        if (user == null) {
            log.info("用户登录失败，传入的用户名：{}，密码：{}", username, password);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在或密码错误");
        }
        UserVO userVO = new UserVO(user);
        response.setHeader(JwtTokenUtils.TOKEN_HEADER, JwtTokenUtils.generateToken(userVO, false));
        return userVO;
    }

    /**
     * 天外天账号登录，先查本地数据库（缓存），没有再发http，之后会存到本地
     *
     * @param username 学号
     * @param password 密码
     * @return 用户信息
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    @Override
    public UserVO tjuUserLogin(String username, String password, HttpServletResponse response) {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        response.setStatus(HttpServletResponse.SC_OK);

        // 先在本地库中查，存在则返回
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + password).getBytes());

        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        queryWrapper.eq("password", encryptPassword);
        User user = userMapper.selectOne(queryWrapper);
        if (user != null) {
            UserVO userVO = new UserVO(user);
            response.setHeader(JwtTokenUtils.TOKEN_HEADER, JwtTokenUtils.generateToken(userVO, false));
            return userVO;
        }

        // 配置请求
        MultiValueMap<String, String> paramMap = new LinkedMultiValueMap<>();
        paramMap.add("account", username);
        paramMap.add("password", password);
        HttpHeaders headers = restTemplateConfig.requestOpencenterHeader();
        HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(paramMap, headers);

        // 发出请求
        JSONObject jsonObject = restTemplate.postForObject(LOGIN_URL, httpEntity, JSONObject.class);
        assert jsonObject != null;

        String errorCode = jsonObject.getStr("error_code");
        String message = jsonObject.getStr("message");
        if (!(errorCode.equals("0") && message.equals("成功"))) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "登录失败");
        }

        JSONObject result = jsonObject.getJSONObject("result");

        user = new User();
        user.setUsername(username);
        user.setPassword(encryptPassword);
        user.setIsTju(1);
        user.setNickname(result.getStr("nickname"));
        //设置随机头像
        user.setAvatar(AvatarUtils.getAvatarUrl());

        // 防止插入多条相同数据
        synchronized (username.intern()) {
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper.eq("username", username);
            Long count = userMapper.selectCount(wrapper);
            // 本地库中没有，则保存
            if (count == 0) {
                int insert = userMapper.insert(user);
                if (insert != 1) {
                    throw new BusinessException(ErrorCode.SERVER_ERROR, "保存用户信息出错");
                }
            }
        }

        UserVO userVO = new UserVO(user);
        response.setHeader(JwtTokenUtils.TOKEN_HEADER, JwtTokenUtils.generateToken(userVO, false));
        return userVO;
    }

    //线程池
    ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);

    /**
     * 向邮箱发送验证码
     *
     * @param email 邮箱
     */
    @Override
    public void sendCode(String email, HttpServletRequest request) {
        HttpSession session = request.getSession();
        //验证码
        String verCode = VerCodeUtils.getVerCode();
        String time = DateUtil.format(new Date(), DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"));
        //验证码和邮箱，一起放入session中
        session.setAttribute("email", email);
        session.setAttribute("code", verCode);
        //创建计时线程池，到时间自动移除验证码
        try {
            scheduledExecutorService.schedule(new Thread(() -> {
                if (session.getAttribute("email").equals(email)) {
                    session.removeAttribute("email");
                    session.removeAttribute("code");
                }
            }), 5 * 60, TimeUnit.SECONDS);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //发送邮件部分
        MimeMessage mimeMessage;
        MimeMessageHelper helper;
        try {
            //发送复杂的邮件
            mimeMessage = mailSender.createMimeMessage();
            //组装
            helper = new MimeMessageHelper(mimeMessage, true);
            //邮件标题
            helper.setSubject("【TWT 方寸流年】 邮箱验证码");
            helper.setText("<h3>\n" +
                    "\t<span style=\"font-size:16px;\">亲爱的用户：</span> \n" +
                    "</h3>\n" +
                    "<p>\n" +
                    "\t<span style=\"font-size:14px;\">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style=\"font-size:14px;\">&nbsp; <span style=\"font-size:16px;\">&nbsp;&nbsp;您好！您正在进行邮箱验证，本次请求的验证码为：<br><span style=\"font-size: 32px;color: rgb(0, 160, 233);display: block;margin-top: 21px;text-align: center;\"> " + verCode + "</span><br>\t<span style=\"font-size:14px;\">&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;&nbsp;本验证码5分钟内有效，请在5分钟内完成验证。（请勿泄露此验证码）如非本人操作，请忽略该邮件。(这是一封自动发送的邮件，请不要直接回复）</span></span>\n" +
                    "</p>\n" +
                    "<p style=\"text-align:right;\">\n" +
                    "\t<span style=\"background-color:#FFFFFF;font-size:16px;color:#000000;\"><span style=\"color:#000000;font-size:16px;background-color:#FFFFFF;\"><span class=\"token string\" style=\"font-family:&quot;font-size:16px;color:#000000;line-height:normal !important;background-color:#FFFFFF;\">TWT工作室</span></span></span> \n" +
                    "</p>\n" +
                    "<p style=\"text-align:right;\">\n" +
                    "\t<span style=\"background-color:#FFFFFF;font-size:14px;\"><span style=\"color:#FF9900;font-size:18px;\"><span class=\"token string\" style=\"font-family:&quot;font-size:16px;color:#000000;line-height:normal !important;\"><span style=\"font-size:16px;color:#000000;background-color:#FFFFFF;\">" + time + "</span><span style=\"font-size:18px;color:#000000;background-color:#FFFFFF;\"></span></span></span></span> \n" +
                    "</p>", true);
            //收件人
            helper.setTo(email);
            //发送方
            helper.setFrom(Objects.requireNonNull(environment.getProperty("spring.mail.username")));
            //发送邮件
            try {
                mailSender.send(mimeMessage);
            } catch (MailException e) {
                e.printStackTrace();
                throw new BusinessException(ErrorCode.SERVER_ERROR, "发送失败--无效邮箱");
            }
        } catch (MessagingException e) {
            e.printStackTrace();
            throw new BusinessException(ErrorCode.SERVER_ERROR, "发送失败--服务器繁忙");
        }
    }

    /**
     * 用户修改密码
     *
     * @param email       邮箱
     * @param newPassword 新密码
     */
    @Override
    public void resetPassword(String email, String newPassword) {
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("username", email);
        User user = userMapper.selectOne(userQueryWrapper);
        if (user == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "该账号不存在");
        }
        User newUser = new User();
        Integer userId = user.getUserId();
        newUser.setUserId(userId);
        newUser.setPassword(DigestUtils.md5DigestAsHex((SALT + newPassword).getBytes()));
        int update = userMapper.updateById(newUser);
        if (update < 1) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "密码修改失败");
        }
    }

    /**
     * 验证邮箱验证码
     *
     * @param email 邮箱
     * @param code  收到的验证码
     * @return 是否验证成功
     */
    @Override
    public boolean verifyCode(String email, String code, HttpServletRequest request) {
        HttpSession session = request.getSession();
        Object oriEmail = session.getAttribute("email");
        Object oriCode = session.getAttribute("code");
        if (oriEmail == null || oriCode == null ||
                !email.equals(oriEmail.toString()) ||
                !code.toUpperCase().equals(oriCode.toString())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "验证码无效或已过期");
        }
        return true;
    }


}




